<?php

namespace app\components;

use app\models\Setting;
use Exception;
use JShrink\Minifier as JsMin;
use tubalmartin\CssMin\Minifier as CssMin;
use Yii;
use yii\base\BootstrapInterface;
use yii\base\Component;
use yii\helpers\FileHelper;
use yii\helpers\Html;
use yii\web\Application;
use yii\web\Response;
use yii\web\View;

class AssetCompress extends Component implements BootstrapInterface {

    public $cssMinify = TRUE;
    public $jsMinify = TRUE;
    public $htmlCompress = TRUE;

    /**
     * Bootstrap method to be called during application bootstrap stage.
     * @param Application $app the application currently running
     */
    public function bootstrap($app) {
        if (!$app instanceof Yii::$app) {
            return;
        }
        $this->cssMinify = Setting::get('enable_css_compress');
        $this->jsMinify = Setting::get('enable_js_compress');
        $this->htmlCompress = Setting::get('enable_html_compress');
        $app->view->on(View::EVENT_END_PAGE, [$this, 'endPage']);

        if ($this->htmlCompress) {
            $app->response->on(Response::EVENT_BEFORE_SEND, [$this, 'beforeSend']);
        }
    }

    public function beforeSend() {
        $request = Yii::$app->request;
        $response = Yii::$app->response;
        if ($response->format === Response::FORMAT_HTML && !$request->isAjax && !$request->isPjax) {
            if (!empty($response->data)) {
                $response->data = $this->compress($response->data);
            }
            if (!empty($response->content)) {
                $response->content = $this->compress($response->content);
            }
        }
    }

    /**
     * @throws \yii\base\Exception
     */
    public function endPage() {
        if (Yii::$app->controller->module->id == 'admin') {
            return;
        }
        $view = Yii::$app->view;
        if ($this->jsMinify && !empty($view->jsFiles)) {
            foreach ($view->jsFiles as $position => $files) {
                $view->jsFiles[$position] = $this->_processingFiles($files, 'js');
            }
        }

        if (!empty($view->cssFiles) && $this->cssMinify) {
            $view->cssFiles = $this->_processingFiles($view->cssFiles, 'css');
        }
    }

    protected function format_url($srcUrl, $baseUrl) {
        $srcInfo = parse_url($srcUrl);
        if (isset($srcInfo['scheme'])) {
            return $srcUrl;
        }
        $baseInfo = parse_url($baseUrl);
        $url = $baseInfo['scheme'] . '://' . $baseInfo['host'];
        if (substr($srcInfo['path'], 0, 1) == '/') {
            $path = $srcInfo['path'];
        } else {
            $filename = basename($baseInfo['path']);
            if (strpos($filename, ".") === FALSE) {
                $path = dirname($baseInfo['path']) . '/' . $filename . '/' . $srcInfo['path'];
            } else {
                $path = dirname($baseInfo['path']) . '/' . $srcInfo['path'];
            }

        }
        $rst = array();
        $path_array = explode('/', $path);
        if (!$path_array[0]) {
            $rst[] = '';
        }
        foreach ($path_array AS $key => $dir) {
            if ($dir == '..') {
                if (end($rst) == '..') {
                    $rst[] = '..';
                } elseif (!array_pop($rst)) {
                    $rst[] = '..';
                }
            } elseif ($dir && $dir != '.') {
                $rst[] = $dir;
            }
        }
        if (!end($path_array)) {
            $rst[] = '';
        }
        $url .= implode('/', $rst);
        return str_replace('\\', '/', $url);
    }

    /**
     * @param $files
     * @param $type
     * @return array
     * @throws \yii\base\Exception
     * @throws Exception
     */
    protected function _processingFiles($files, $type) {
        $data = $result = [];
        foreach ($files as $fileCode => $fileTag) {
            $path = parse_url($fileCode, PHP_URL_PATH);
            if (FALSE === $path) {
                continue;
            }
            $absoluteFile = Yii::getAlias("@webroot{$path}");
            if (file_exists($absoluteFile)) {
                $code = file_get_contents($absoluteFile);
                if ($type == 'css') {
                    $code = preg_replace_callback(
                        '|url\(([^)]*)|',
                        function ($matches) use ($fileCode){
                            $url = parse_url($this->format_url($matches[1], $fileCode), PHP_URL_PATH);
                            return "url($url";
                        },
                        $code
                    );
                }
                $data[] = $code;
            } else {
                $result[$fileCode] = $fileTag;
            }
        }
        $hash = substr(md5(serialize($data)), 8, 16);
        $file = Yii::getAlias("@theme_root/{$type}/{$hash}.{$type}");
        $fileUrl = Yii::getAlias("@theme/{$type}/{$hash}.{$type}");

        $result[$fileUrl] = $type === 'js' ? Html::jsFile($fileUrl) : Html::cssFile($fileUrl);
        if (file_exists($file)) {
            return $result;
        }

        $data = join("\n", $data);
        if ($type === 'js') {
            $data = JsMin::minify($data);
        } else {

            $compressor = new CssMin();
            $compressor->keepSourceMapComment();

            $compressor->removeImportantComments();
            $data = $compressor->run($data);
        }

        FileHelper::createDirectory(dirname($file));
        file_put_contents($file, $data);

        return $result;
    }

    protected function request($url) {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_REFERER, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        $result = curl_exec($ch);
        if (curl_errno($ch)) {
            $result = FALSE;
        }
        curl_close($ch);
        return $result;
    }

    public function compress($html) {
        $filters = array(
            '/(?:<script[^>]*>|\G(?!\A))(?:[^\'"\/<]+|"(?:[^\\"]+|\\.)*"|\'(?:[^\\\']+|\\.)*\'|\/(?!\/)|<(?!\/script))*+\K\/\/[^\n|<]*/xsu' => '',
            '/<!--(?!\s*(?:\[if [^\]]+]|<!|>|Generated))(?:(?!-->).)*-->/su' => '',
            '/\/+?\s*\*[\s\S]*?\*\s*\/+/u' => '',
            '/>\s{2,}</u' => '><',
            '/\s{2,}/u' => ' ',
            '/(\r?\n)/u' => '',
        );
        $output = preg_replace(array_keys($filters), array_values($filters), $html);
        return $output;
    }
}